package com.iot08.api.interceptor;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.iot08.api.annotation.ApiLimit;
import com.iot08.api.config.redis.RedisClusterService;
import com.iot08.common.utils.response.MyHttpServlet;
import com.iot08.common.utils.response.ResponseEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.net.InetAddress;

/**
 *
 */
@Slf4j
public class ApiLimitInterceptorService {

    /**
     * 接口访问次数限制处理类
     *
     * @param request
     * @param response
     * @param handler
     * @return
     */
    public static boolean ApiLimitHandler(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod hm = (HandlerMethod) handler;
            Class<?> clazz = hm.getBeanType();
            Method m = hm.getMethod();
            String clazzName = clazz.getName();
            String methodName = m.getName();
            if (clazz != null && m != null) {
                //下：判断当前注解 在类上、接口上是否存在
                boolean isClzAnnotation = clazz.isAnnotationPresent(ApiLimit.class);
                boolean isMethodAnnotation = m.isAnnotationPresent(ApiLimit.class);
                if (isClzAnnotation || isMethodAnnotation) {
                    ApiLimit apiLimit = null;
                    //如果方法和类中同时声明这个注解，那么方法中的注解会覆盖类中的设定
                    if (isMethodAnnotation) {
                        apiLimit = m.getAnnotation(ApiLimit.class);
                    } else if (isClzAnnotation) {
                        apiLimit = clazz.getAnnotation(ApiLimit.class);
                    }

                    String limitKey = null;
                    int visitedTimesMax = apiLimit.times_max();//该接口最大访问次数
                    int timeToLive = (int)apiLimit.timeToLive();//存活时间
                    String prefixKey = request.getServletPath();
                    String suffixKey = "";
                    //限制级别
                    if (apiLimit.limitLevel() == ApiLimit.LimitLevel.API_LEVEL) {//接口级别
                        limitKey = prefixKey;
                    } else { //用户级别
                        String suffixKeyFrom = apiLimit.suffixKeyFrom();
                        if (!StringUtils.isEmpty(suffixKeyFrom)) {//如果指定了后缀
                            if (suffixKeyFrom.equals("access_token")) {// access_token 在app模块保存在header中
                                String authorization = request.getHeader("Authorization");
                                String[] tokenArr = authorization.split(" ");
                                suffixKey = tokenArr[1];
                            } else {
                                suffixKey = request.getParameter(suffixKeyFrom);

                                //针对suffixKeyFrom 为mobile 并且 prefixKey = /api/common/msg.json
                                //根据IP限制2个小时最多1次
                                if ("mobile".equals(suffixKeyFrom) && prefixKey.contains("api/common/msg")) {
                                    String limitIp = prefixKey + getIpAddr(request);
                                    if (isTooFrequent(limitIp, 10, 86400)) {
                                        try {
                                            log.error("超出访问频率限制（IP限制24个小时最多10次）, limitKey:{}", limitIp);
                                            MyHttpServlet.response(response, ResponseEnum.STATUS5006);
                                        } catch (JsonProcessingException e) {
                                            log.error("频率限制:", e.toString());
                                            e.printStackTrace();
                                        }
                                        return false;
                                    }
                                }
                            }
                        }
                        limitKey = prefixKey + suffixKey;//前缀=请求的相对路径 后缀=用户级别指定的key(可能是mobile、AccessToken、email等)
                    }


                    /*----------------------- 校验是否频次太高 -----------------------*/
                    if (isTooFrequent(limitKey, visitedTimesMax, timeToLive)) {
                        try {
                            log.error("超出访问频率限制，limitKey:{},visitedTimesMax:{}", limitKey, visitedTimesMax);
                            MyHttpServlet.response(response, ResponseEnum.STATUS5006);
                        } catch (JsonProcessingException e) {
                            log.error("频率限制:", e.toString());
                            e.printStackTrace();
                        }
                        return false;
                    }
                }
            }
        }
        return true;
    }


    /**
     * 频次超出指定大小
     * true:超出
     */
    public static boolean isTooFrequent(String api_key, int visitedTimesMax, int timeToLive) {
        RedisClusterService redisClusterService = new RedisClusterService();
        Long visitedTimes = Long.valueOf(redisClusterService.get(api_key));//访问次数
        if (visitedTimes == null) {//在该时间段是第一次访问
            visitedTimes = 1L;
            redisClusterService.setex(api_key, timeToLive, ""+visitedTimes);//存入redis，在 timeToLive 之后失效
            return false;
        } else {
            if (visitedTimes >= visitedTimesMax) {//超出
                return true;
            } else {
                redisClusterService.updateValue(api_key, "" + (++visitedTimes) );
                return false;
            }
        }
    }

    /**
     * 获取客户端IP地址
     */
    private static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            if (ip.equals("127.0.0.1")) {
                //根据网卡取本机配置的IP
                InetAddress inet = null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                ip = inet.getHostAddress();
            }
        }
        // 多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
        if (ip != null && ip.length() > 15) {
            if (ip.indexOf(",") > 0) {
                ip = ip.substring(0, ip.indexOf(","));
            }
        }
        return ip;
    }

}
